//#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define D3D_DEBUG_INFO

//memory leak
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include <windows.h>
#include <d3dx9.h>

#define WND_WIDTH 1024
#define WND_HEIGHT 789

#include "UserInterface.h"
#include "WorldModel.h"
#include "Visualisation.h"
#include "AI.h"
#include "Sound.h"
#include <TXF.h>
#include <sstream>
using namespace TXF;

//callback function that handle message
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			break;

		default: //handle the message that we don't want in the default way
			return DefWindowProc(hWnd,message,wParam, lParam);	
	}
	return 0;
}

/*entry point of the application*/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{	
	//memory leak detection
	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

	//tick variable
	DWORD numberOfTick=50;
	DWORD lastTick=timeGetTime();

	//intro screen variables
	DWORD timeBeginDisplayIntroScreen=(DWORD)-1;
	//float durationDisplayIntroScreen=4000;
	float durationDisplayIntroScreen=4000;
	bool introScreenDisplayed=false;

	//game over variables
	bool gameOver=false;
	DWORD timeBeginDisplayGameOver=(DWORD)-1;
	float durationDisplayGameOver=4000;

	//define and fill a window class
	WNDCLASSEX myWindowClass;

	myWindowClass.cbSize=sizeof(WNDCLASSEX);
	myWindowClass.style=CS_HREDRAW|CS_VREDRAW;
	myWindowClass.lpfnWndProc=(WNDPROC)WndProc;
	myWindowClass.cbClsExtra=0;
	myWindowClass.cbWndExtra=0;
	myWindowClass.hInstance=hInstance;
	myWindowClass.hIcon=0;
	myWindowClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	myWindowClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
	myWindowClass.lpszMenuName=0;
	myWindowClass.lpszClassName="MyWindowClass";
	myWindowClass.hIconSm=0;

	//create the window
	if(!VIS->createWindow(WND_WIDTH, WND_HEIGHT, &myWindowClass, hInstance, nCmdShow))
		exit(1);

	//initialisation of directx
	if(!VIS->initDirectX())
		exit(-1);

	VIS->setWireframeMode(false);

	//open the xml file
	Document dataDocument;
	try
	{
		dataDocument.Read(".\\data\\data.xml");
	}
	catch(TXF::InvalidFileFormatException) //if the file doesn't exist or is not a well formed xml file
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot open the data.xml file", "ERROR",0);
		exit(-1);
	}
	

	//get the root and all the children
	Element root =dataDocument.RootElement();
	list<Element> data=root.Children();
	
	//get the models from the xml file
	list<Element>::iterator iteratorDataFile=data.begin();
	if(iteratorDataFile->Name()!="VIS")
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the models from the XML file", "ERROR",0);
		return -1;
	}
	list<Element> dataVis=iteratorDataFile->Children();
	list<Element>::iterator iteratorDataVis=dataVis.begin();

	bool errorLoading=false;
	int indexSkybox;
	if(!VIS->loadSkyBox(&indexSkybox, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load skybox", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexZombie;
	if(!VIS->loadModel(&indexZombie, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load zombie", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexObelisque;
	if(!VIS->loadModel(&indexObelisque, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load obelisque", "ERROR",0);
		errorLoading=true;
	}

	//load the terrain and convert the string from the xml into int
	iteratorDataVis++;
	list<Element> bounds=iteratorDataVis->Children();
	list<Element>::iterator iteratorBound=bounds.begin();
	stringstream ss;
	ss.str(iteratorBound->Attribute("x"));
	D3DXVECTOR3 minBound;
	ss>>minBound.x;
	ss.clear();
	ss.str(iteratorBound->Attribute("y"));
	ss>>minBound.y;
	ss.clear();
	ss.str(iteratorBound->Attribute("z"));
	ss>>minBound.z;
	iteratorBound++;
	D3DXVECTOR3 maxBound;
	ss.clear();
	ss.str(iteratorBound->Attribute("x"));
	ss>>maxBound.x;
	ss.clear();
	ss.str(iteratorBound->Attribute("y"));
	ss>>maxBound.y;
	ss.clear();
	ss.str(iteratorBound->Attribute("z"));
	ss>>maxBound.z;
	int numCellWide;
	ss.clear();
	ss.str(iteratorDataVis->Attribute("numCellWide"));
	ss>>numCellWide;
	int numCellHigh;
	ss.clear();
	ss.str(iteratorDataVis->Attribute("numCellHigh"));
	ss>>numCellHigh;

	int indexTerrain;
	if(!VIS->loadTerrain(&indexTerrain, numCellWide, numCellHigh, minBound, maxBound))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load terrain", "ERROR",0);
		errorLoading=true;
	}
	if(!VIS->createFont())
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot create the font", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexMenu;
	if(!VIS->loadTexture(&indexMenu, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the texture", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexGrave;
	if(!VIS->loadModel(&indexGrave, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the GraveStone", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexChurch;
	if(!VIS->loadModel(&indexChurch, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the GraveStone", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexDog;
	if(!VIS->loadModel(&indexDog, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the Dog", "ERROR",0);
		errorLoading=true;
	}
	iteratorDataVis++;
	int indexPlayer;
	if(!VIS->loadModel(&indexPlayer, iteratorDataVis->Content()))
	{
		MessageBox(VIS->getWindowHandle(), "Error: cannot load the player", "ERROR",0);
		errorLoading=true;
	}
	if(errorLoading)
	{
		VIS->close();
		exit(1);
	}

	D3DXVECTOR3 directionOfLight(1, -1, 0);
	VIS->createDirectionalLight(&directionOfLight, 0);

	//load sound
	iteratorDataFile++;
	list<Element> dataSound=iteratorDataFile->Children();
	list<Element>::iterator iteratorSound=dataSound.begin();
	SOUND->initialise();
	int zombie1;
	SOUND->addSound(&zombie1, iteratorSound->Content());
	iteratorSound++;
	int zombie2;
	SOUND->addSound(&zombie2, iteratorSound->Content());
	iteratorSound++;
	int zombie3;
	SOUND->addSound(&zombie3, iteratorSound->Content());
	iteratorSound++;
	int zombie4;
	SOUND->addSound(&zombie4, iteratorSound->Content());
	iteratorSound++;
	int scream;
	SOUND->addSound(&scream, iteratorSound->Content());
	iteratorSound++;
	int dog1;
	SOUND->addSound(&dog1, iteratorSound->Content());
	iteratorSound++;
	int dog2;
	SOUND->addSound(&dog2, iteratorSound->Content());
	iteratorSound++;
	int dog3;
	SOUND->addSound(&dog3, iteratorSound->Content());

	iteratorSound++;
	SOUND->addBackSound(iteratorSound->Content());

	//Create the entities
	WORLDMODEL->createCamera(20, 5); //must be the FIRST entity!!!!!!!!!!!!!!

	//create the skybox first, like this it is render first
	WORLDMODEL->createSkyBox(indexSkybox);
	WORLDMODEL->createTerrain(indexTerrain);
	WORLDMODEL->createPlayer(indexPlayer, D3DXVECTOR3(-10, 5, -10), 0, 0, 0, 1, 100, true);

	//zombies
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-80, 1, -50), 0, 0, 0, 2, true, zombie1, 50000, 1, (float)1.1);
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-80, 1, -45), 0, 0, 0, 2, true, zombie2, 15000, 2, (float)1.1);
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-80, 1, -30), 0, 0, 0, 2, true, zombie3, 12000, 3, (float)1.1);
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-80, 1, -40), 0, 90, 0, 2, true, zombie4, 10000, 4, (float)1.1);
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-80, 1, -55), 0, 180, 0, 2, true, zombie3, 25000, 5, (float)1.1);
	WORLDMODEL->createEnemy(indexZombie, D3DXVECTOR3(-85, 1, -30), 0, 45, 0, 2, true, zombie2, 35000, 6, (float)1.1);

	//oblisque in the four corner
	WORLDMODEL->createDecor(indexObelisque, D3DXVECTOR3(-90, 0, -90), 0, 0, 0, true, -2);
	WORLDMODEL->createDecor(indexObelisque, D3DXVECTOR3(-90, 0, 90), 0, 0, 0, true, -2);
	WORLDMODEL->createDecor(indexObelisque, D3DXVECTOR3(90, 0, -90), 0, 0, 0, true, -2);
	WORLDMODEL->createDecor(indexObelisque, D3DXVECTOR3(90, 0, 90), 0, 0, 0, true, -2);

	//dogs
	WORLDMODEL->createEnemy(indexDog, D3DXVECTOR3(50, 0, -40), 0, 45, 0, 7, true, dog1, 35000, 10, (float)0.7);
	WORLDMODEL->createEnemy(indexDog, D3DXVECTOR3(53, 0, -42), 0, 45, 0, 7, true, dog3, 10000, 10, (float)0.7);
	WORLDMODEL->createEnemy(indexDog, D3DXVECTOR3(48, 0, -40), 0, 45, 0, 7, true, dog2, 25000, 10, (float)0.7);
	WORLDMODEL->createEnemy(indexDog, D3DXVECTOR3(46, 0, -30), 0, 45, 0, 7, true, dog2, 50000, 10, (float)0.7);
	WORLDMODEL->createEnemy(indexDog, D3DXVECTOR3(48, 0, -35), 0, 45, 0, 7, true, dog1, 40000, 10, (float)0.7);

	//grave
	for(int j=0; j<13; j++)
	{
		for(int i=0; i<6; i++)
		{
			WORLDMODEL->createDecor(indexGrave, D3DXVECTOR3((float)(-60+j*10), 0, (float)(i*15)), 0, 0, 0, true, (float)0.4);
		}
	}

	//church
	WORLDMODEL->createDecor(indexChurch, D3DXVECTOR3(0, 0, -60), 0, (float)3.14, 0, true, 0);

	//set camera mode
	WORLDMODEL->setCameraMode(CWorldModel::eModeTPS);

	//initialise UI
	if(!UI->initialiseInputs(hInstance))
	{
		UI->close();
		VIS->close();
		WORLDMODEL->close();
		SOUND->close();
	}
	if(!UI->loadActionMapping(".\\data\\control.xml"))
	{
		UI->close();
		VIS->close();
		WORLDMODEL->close();
		SOUND->close();
	}
	UI->initialiseMenu(indexMenu);

	//message loop
	MSG msg;
	ZeroMemory(&msg, sizeof(msg));

	SOUND->playBackSound();
	//display the intro screen
	while(!introScreenDisplayed)
	{
		if(timeBeginDisplayIntroScreen==-1)
			timeBeginDisplayIntroScreen=timeGetTime();

		if(timeGetTime()<=timeBeginDisplayIntroScreen+durationDisplayIntroScreen)
		{
			VIS->getDevice()->Clear(0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0),1.0,0);
			HRESULT hr=VIS->getDevice()->BeginScene();
			if(SUCCEEDED(hr))
			{
				UI->displayMenu();
				VIS->getDevice()->EndScene();
				VIS->getDevice()->Present(0, 0, 0, 0);
			}
		}

		else
			introScreenDisplayed=true;
	}

	HRESULT hr;
	while(msg.message!=WM_QUIT && !gameOver) 
	{
		if(PeekMessage(&msg, 0, 0U, 0U, PM_REMOVE))
		{
			//translate the message into a character message
			TranslateMessage(&msg);
			//give the message to the window procedure that we defines in the window class
			DispatchMessage(&msg);
		}
		else
		{
			//update if enough time has passed
			if(timeGetTime()>lastTick+numberOfTick)
			{
				UI->doUpdateInputs();
				WORLDMODEL->doUpdate();
				lastTick=timeGetTime();
			}
			
			//clear the screen
			VIS->getDevice()->Clear(0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0),1.0,0);
			hr=VIS->getDevice()->BeginScene();
			if(SUCCEEDED(hr))
			{
				if(!WORLDMODEL->getGameOver())//if the player is alive (not game over)
				{
					//display the scene
					WORLDMODEL->doRender();

					//display the hud
					UI->displayHUD();
				}
				else //display the game over screen
				{
					if(timeBeginDisplayGameOver==-1)
						timeBeginDisplayGameOver=timeGetTime();

					if(timeGetTime()<=timeBeginDisplayGameOver+durationDisplayGameOver)
						UI->displayGameOver();
					else
						gameOver=true;
				}
				VIS->getDevice()->EndScene();
				VIS->getDevice()->Present(0, 0, 0, 0);
			}
		}
	}

	//deletion of the components
	VIS->close();
	WORLDMODEL->close();
	UI->close();
	AI->close();
	SOUND->close();
	
	return (int)msg.wParam;

	
}

